#include "stdafx.h"

#include "parser_internal.h"
#include "parser_state.h"
#include "parser_errors.h"

#include <lean/functional/algorithm.h>

#include "ast_types.h"
#include "ast_expressions.h"

namespace funpl
{

namespace parser
{

struct type_modifiers
{
	bool const_;

	/// Constructor.
	type_modifiers()
		: const_() { }
};

/// Applies the given modifiers to the given type.
ast::type* apply_modifiers(ast::type *type, const type_modifiers &modifiers)
{
	type->const_ = modifiers.const_;
	return type;
}

/// Parses type modifiers.
type_modifiers maybe_parse_type_modifiers(parser_state &state, bool implicitSemicolons = true)
{
	type_modifiers modifiers;

	// Context-sensitive modifiers
	while (check(state, token_type::tok_identifier, implicitSemicolons))
	{
		// Const
		if (lean::equal(state.token.range, lean::make_char_range("const")))
			modifiers.const_ =  true;
		// Unknown modifier, stop
		else
			break;

		// Valid modifier
		accept(state);
	}

	return modifiers;
}

/// Parses a list of template arguments.
ast::metaarguments* parse_metaarguments(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	// Optional parentheses
	// NOTE: Template arguments EXPECTED, always skip first line break
	if (accept_cont(state, token_type::tok_oparen))
	{
		// Nested argument list
		auto args = parse_metaarguments(state, parent, false);

		// Make sure all arguments have been parsed
		expect_cont(state, token_type::tok_cparen);

		return args;
	}
	else
	{
		auto args = state.tree->create_node<ast::metaarguments>(parent);

		// Arguments
		do
		{
			// Type expression
			if (accept_cont(state, token_type::op_assign))
				args->arguments.push_back( parse_expression(state, args, implicitSemicolon) );
			else
				args->arguments.push_back( parse_type_expression(state, args, implicitSemicolon) );
		}
		while (accept_cont(state, token_type::tok_comma));

		return args;
	}
}

/// Parses a type.
ast::type* parse_type_name(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	auto type = state.tree->create_node<ast::type_identifier>(parent);

	// Name
	type->name = expect(state, token_type::tok_identifier, implicitSemicolon).token.range;

	// Template arguments
	if (accept_cont(state, token_type::op_on))
		type->arguments = parse_metaarguments(state, type, implicitSemicolon);

	// Nested types
	if (accept_cont(state, token_type::op_period) || accept_cont(state, token_type::tok_op_colon))
		type->nested = parse_type_name(state, parent, implicitSemicolon);

	return type;
}

/// Parses a function type.
ast::function_type* parse_function_type(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_fun);

	auto type = state.tree->create_node<ast::function_type>(parent);

	// Reference function
	if (accept_cont(state, token_type::op_and))
		type->returnRef = true;

	// Parameters
	expect_cont(state, token_type::tok_oparen);

	// Allow for 0 parameters
	if (!check_cont(state, token_type::tok_cparen))
		do
		{
			auto parameterType = parse_type(state, type, false);
			type->parameters.push_back(parameterType);
		}
		while (accept_cont(state, token_type::tok_comma));

	expect_cont(state, token_type::tok_cparen);

	// Return type
	if (accept_cont(state, token_type::op_to))
		type->returnType = parse_type(state, type, implicitSemicolon);

	return type;
}

/// Parses a type.
ast::type* parse_type(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	// Optional parentheses
	if (accept_cont(state, token_type::tok_oparen))
	{
		// Nested argument list
		auto type = parse_type(state, parent, false);

		// Make sure all arguments have been parsed
		expect_cont(state, token_type::tok_cparen);

		return type;
	}
	else
	{
		// Modifiers
		type_modifiers modifiers = maybe_parse_type_modifiers(state, implicitSemicolon);

		ast::type *type = nullptr;

		if (accept(state, token_type::tok_questionmark, implicitSemicolon))
			// Inline meta-type definition
			type = state.tree->create_node<ast::inline_metatype>(parent, parse_metatype(state, parent, implicitSemicolon));
		else if (check(state, token_type::kw_fun, implicitSemicolon))
			// Function type
			type = parse_function_type(state, parent, implicitSemicolon);
		else
			// Base type
			type = parse_type_name(state, parent, implicitSemicolon);

		apply_modifiers(type, modifiers);

		while (true)
		{
			// Modifiers
			type_modifiers modifiers = maybe_parse_type_modifiers(state);

			ast::type *typeCtor = nullptr;

			// Pointer type
			if (accept(state, token_type::op_mul, implicitSemicolon))
				typeCtor = state.tree->create_node<ast::pointer_type>(parent, type);
			// Array type
			else if (accept(state, token_type::tok_obracket, implicitSemicolon))
			{
				ast::expression *subscriptExpr = nullptr;

				// Allow for arrays of unknown size
				if (!check_cont(state, token_type::tok_cbracket))
					subscriptExpr = parse_expression(state, parent, false);

				typeCtor = state.tree->create_node<ast::array_type>(parent, subscriptExpr);

				expect_cont(state, token_type::tok_cbracket);
			}
			else
				break;

			// Construct compound type
			type->parent = apply_modifiers( LEAN_ASSERT_NOT_NULL(typeCtor), modifiers );
			type = typeCtor;
		}

		return type;
	}
}

} // namespace

} // namespace